import re

class section:
	def __init__(self, string):
	#	if(string[0] == "[" and string[len(string)-1] == "]"): #Replaced when phi added
		if(string[0] == "<" and string[len(string)-1] == ">"):
			self.required = False
			string = string[1:-1]
		else:	
			self.required = True
			
		self.string = string
		self.targetCount = 0
		self.firstTarget = 0
		
		self.sourceCount = 0
		self.firstSource = 0
		
		self.typeCount = 0
		self.firstType = 0
		
		self.labelCount = 0
		self.firstLabel = 0
		
		self.nameCount = 0
		self.firstName = 0
		
		self.replacementDict = {"target":[self.targetCount, self.firstTarget],
  								 "source":[self.sourceCount, self.firstSource],
  								 "type":[self.typeCount, self.firstType],
  								 "label":[self.labelCount, self.firstLabel],
  								 "name":[self.nameCount, self.firstName]}
	
	def replaceVar(self, initialIndex, name):
		theseVars = self.replacementDict[name]
		theseVars[0] = len(re.findall("\|"+str(name)+"\|", self.string))
		theseVars[1] = initialIndex
		for i in range(theseVars[1], (theseVars[1] + theseVars[0])):
			self.string = re.sub("\|"+str(name)+"\|", "self." + str(name) + str(i), self.string,1)
		
		return theseVars[1] + theseVars[0]
	
	def getRequired(self):
		return self.required
		
	def getString(self):
		return self.string
		
	def getVars(self, name):
		varList = []
		for i in range(self.replacementDict[name][1], self.replacementDict[name][1] + self.replacementDict[name][0]):
			varList.append(name + str(i))
		return varList
	
	def getAllVars(self):
		targets =  self.getVars("target")
		sources =  self.getVars("source")
		types = self.getVars("type")
		labels = self.getVars("label")
		names = self.getVars("name")
		retVal = []
		if(targets != None):
			retVal.extend(targets)
		if(sources != None):
			retVal.extend(sources)
		if(types != None):
			retVal.extend(types)
		if(labels != None):
			retVal.extend(labels)
		if(names != None):
			retVal.extend(names)
		return retVal
	
	def getQualityCount(self, name):
		return self.replacementDict[name][0]


class llvmGenerator:
	def __init__(self, commmandString, className, ):
		self.sections = []
		self.targets = 0
		self.sources = 0
		self.types = 0
		self.labels = 0
		self.names = 0
#		splitOnOption = re.split(r"(\[[^\[]*\])", commmandString) #Replaced due to addition of phi instruction
		splitOnOption = re.split(r"(<[^<]*>)", commmandString)
		for s in splitOnOption:
			if (len(s) > 0):
				sec =  section(s)
				self.targets = sec.replaceVar(self.targets, "target")
				self.sources = sec.replaceVar(self.sources, "source")
				self.types = sec.replaceVar(self.types, "type")
				self.labels = sec.replaceVar(self.labels, "label")
				self.names = sec.replaceVar(self.names, "name")
				self.sections.append(sec)
			
		
			

		self.command = commmandString
		self.className = className
		self.varNameBases = ["target", "source", "label", "name", "type"]
		self.variableCounts = [self.targets, self.sources, self.labels, self.names, self.types]

	def generateCode(self, outputFilePtr):
		f = outputFilePtr
		#class def
		f.write("class " + str(self.className) + ":\n")
		
		#init
		f.write("\tdef __init__(self, " + self.initArgs() + "):\n")
		listOfvars = self.getVars()
		f.write("\t\tglobal instructionID\n")
		f.write("\t\tself.ID = instructionID\n")
		f.write("\t\tinstructionID += 1\n")
		for var in listOfvars:
			f.write("\t\tself." + var + " = " + var + "\n")
		f.write("\t\tself.comment = None")
		
		#set a comment
		f.write("\n\n\tdef setComment(self, comment):\n")
		f.write("\t\tself.comment = comment\n")
		
		#global ID junk
		f.write("\n\n\tdef getID(self):\n")
		f.write("\t\treturn self.ID\n")
		
		if(self.targets != 0):
			#get targets
			f.write("\n\tdef getTarget(self):\n")
			if(self.targets == 1):
				f.write("\t\treturn self.target0\n")
			else: #change this later maybe
				f.write("\t\treturn self.target0\n")
			
			#set target
			f.write("\n\tdef setTarget(self, newTarget):\n")
			f.write("\t\tself.target0 = newTarget\n")
		
		if(self.sources != 0):
			#set source
			f.write("\n\tdef setSource(self, number, newSource):\n")
			for i in range(self.sources):
				if(i == 0):
		 	 	 	f.write("\t\tif(number == " +str(i)+"):\n")
				else:
					f.write("\t\telif(number == " +str(i)+"):\n")
				f.write("\t\t\tself.source" + str(i) +" = newSource\n")
			
			#get sources
			f.write("\n\tdef getSources(self):\n")
			string = "\t\treturn ["
			first = True
			for i in range(self.sources):
				if(not first):
					string += ", "
				else:
					first = False
				string += "self.source" + str(i)
			string += "]"
			f.write(string)
		
		if(self.types != 0):
			#set types
			f.write("\n\tdef setType(self, number, newType):\n")
			first = True
			for i in range(self.types):
				if(i == 0):
		 	 	 	f.write("\t\tif(number == " +str(i)+"):\n")
				else:
					f.write("\t\telif(number == " +str(i)+"):\n")
				f.write("\t\t\tself.type" + str(i) +" = newType\n")
			
			#get types	
			f.write("\n\tdef getTypes(self):\n")
			string = "\t\treturn ["
			first = True
			for i in range(self.types):
				if(not first):
					string += ", "
				else:
					first = False
				string += "self.type" + str(i)
			string += "]\n"
			f.write(string)
		
		#output LLVM
		f.write("\n\tdef toLLVM(self):\n")
		llvmStr = self.command
		patterns = ["\|target\|","\|source\|", "\|label\|", "\|type\|"]
		f.write("\t\tretStr = \"\\t\"\n")
		for sec in self.sections:
			if(sec.getRequired()): # throw that crap in!
				f.write("\t\tretStr += " + sec.getString() + "\n")
			else:
				requirements = sec.getAllVars()
				condStr = "\t\tif("
				first = True
				for r in requirements:
					if(not first):
						condStr += " and "
					else:
						first = False
					condStr += "self." + r + " != None"
				condStr += "):\n"
				f.write(condStr)
				f.write("\t\t\tretStr += " + sec.getString() + "\n")			
				
		f.write("\t\tif(self.comment != None):\n")
		f.write("\t\t\tretStr += \" ; \" + " + "str(self.comment)\n")
		f.write("\t\treturn retStr")
		
		f.write("\n\n\tdef __str__(self):\n")
		f.write("\t\treturn self.toLLVM()+ \"\\n\"")
		f.write("\n\n")
		
#		if(self.className =="llvm_gep"):
#			f.write("\t\tretStr = ")
#		else:
#			for varcount, varPattern in zip(self.variableCounts, patterns):
#				for i in range(varcount):
#					patternsIndex = patterns.index(varPattern)
#					llvmStr = re.sub(varPattern, "self."+self.varNameBases[patternsIndex] + str(i), llvmStr,1)
#			
#			f.write("\t\tif(self.comment == None):\n")
#			f.write("\t\t\treturn \"\\t\" + " +llvmStr + "\n")
#			f.write("\t\telse:\n")
#			f.write("\t\t\treturn \"\\t\" + " +llvmStr + " + \" ; \" + " + "str(self.comment)\n" )
#		

		
		#space before next class
		
		
		
	def getVars(self):
		retlist = self.getRequiredVars()
		if(self.getOptionalSections() != None):
			retlist.extend(self.getOptionalVars())
		return retlist
	
	def getRequiredVars(self):
		
		req = self.getRequiredSections()
		
		retList = []
		self.varNameBases
		for var in self.varNameBases:
			for r in req:
				retList.extend(r.getVars(var));
		
		return retList
	
	def getOptionalVars(self):
		opt = self.getOptionalSections()
		retList = []
		for r in opt:
			for var in self.varNameBases:
				retList.extend(r.getVars(var));		
				
		return retList
	
	def initArgs(self):
		retStr = ""
		listOfArgs = self.getRequiredVars()
		first = True
		
		for arg in listOfArgs:
			if(first == False):
				retStr += ", "
			else:
				first = False
			retStr += arg
			if(arg[:-1] == "type"):
				retStr += " = \"i32\""
		
		listOfArgs = self.getOptionalVars()
		for arg in listOfArgs:
			if(first == False):
				retStr += ", "
			else:
				first = False
			if(arg[:-1] == "type"):
				retStr += arg + " = \"i32\""
			else:
				retStr += arg + " = None"
		
		return retStr		
		
	def getRequiredSections(self):
		result = []
		for s in self.sections:
			if(s.getRequired()):
				result.append(s)
		return result
	
	def getOptionalSections(self):
		result = []
		for s in self.sections:
			if(not s.getRequired()):
				result.append(s)
		return result

handle = open("llvm.py", 'w')
		
handle.write("import sys\n")
handle.write("sys.path.append(sys.path[0] + \"/../interpreter\")\n")
handle.write("\ninstructionID = 0\n")


# "str(|target|) + \" = getelementptr \" + str(|type|) + \" \" + str(|source|) [+\", \" + str(|type|) + \" \" + str(|source|)] [+\", \" + str(|type|) + \" \" + str(|source|)]

#add
llvmGenerator("str(|target|) + \" = add \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_add").generateCode(handle)

#sub
llvmGenerator("str(|target|) + \" = sub \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_sub").generateCode(handle)

#and
llvmGenerator("str(|target|) + \" = and \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_and").generateCode(handle)

#or
llvmGenerator("str(|target|) + \" = or \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_or").generateCode(handle)

#div int
llvmGenerator("str(|target|) + \" = sdiv \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_idiv").generateCode(handle)

#div float
llvmGenerator("str(|target|) + \" = fdiv \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_fdiv").generateCode(handle)


#xor
llvmGenerator("str(|target|) + \" = xor \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_xor").generateCode(handle)

#icmp eq
llvmGenerator("str(|target|) + \" = icmp eq i32 \" + str(|source|) +\" , \" + str(|source|)", "llvm_icmp_eq").generateCode(handle)

#icmp lt
llvmGenerator("str(|target|) + \" = icmp slt i32 \" + str(|source|) +\" , \" + str(|source|)", "llvm_icmp_slt").generateCode(handle)

#icmp less than or equal
llvmGenerator("str(|target|) + \" = icmp sle i32 \" + str(|source|) +\" , \" + str(|source|)", "llvm_icmp_sle").generateCode(handle)

#fcmp oeq
llvmGenerator("str(|target|) + \" = fcmp oeq float \" + str(|source|) +\" , \" + str(|source|)", "llvm_fcmp_eq").generateCode(handle)

#fcmp lt
llvmGenerator("str(|target|) + \" = fcmp olt float \" + str(|source|) +\" , \" + str(|source|)", "llvm_fcmp_slt").generateCode(handle)

#fcmp less than or equal
llvmGenerator("str(|target|) + \" = fcmp ole float \" + str(|source|) +\" , \" + str(|source|)", "llvm_fcmp_sle").generateCode(handle)

#br conditional
llvmGenerator("\" br i1 \" + str(|source|) + \", label %\" + str(|label|) +\" , label %\" + str(|label|)", "llvm_brc").generateCode(handle)

#br
llvmGenerator("\" br label %\" + str(|label|) ", "llvm_br").generateCode(handle)

#malloc
llvmGenerator("str(|target|) + \" = malloc \" + str(|type|) + \", align 4\"", "llvm_malloc").generateCode(handle)

#store
llvmGenerator("\" store \" + str(|type|) + \" \" + str(|source|) + \", \" + str(|type|) + \"* \" + str(|source|)", "llvm_store").generateCode(handle)

#load
llvmGenerator("str(|target|) + \" = load \" + str(|type|) + \"* \" + str(|source|)", "llvm_load").generateCode(handle)

#shift right
llvmGenerator("str(|target|) + \" = lshr \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_lshr").generateCode(handle)

#shift left
llvmGenerator("str(|target|) + \" = shl \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_shl").generateCode(handle)

#mult
llvmGenerator("str(|target|) + \" = mul \" + str(|type|) + \" \" + str(|source|) +\" , \" + str(|source|)", "llvm_mult").generateCode(handle)

#sign extend one
llvmGenerator("str(|target|) + \" = sext \" + str(|type|) + \" \" + str(|source|) +\" to \" + str(|type|)", "llvm_sext").generateCode(handle)

#sign extend one
llvmGenerator("str(|target|) + \" = zext \" + str(|type|) + \" \" + str(|source|) +\" to \" + str(|type|)", "llvm_zext").generateCode(handle)

#bitcast
llvmGenerator("str(|target|) + \" = bitcast \" + str(|type|) + \" \" + str(|source|) +\" to \" + str(|type|)", "llvm_bitcast").generateCode(handle)

#int -> ptr
llvmGenerator("str(|target|) + \" = inttoptr \" + str(|type|) + \" \" + str(|source|) +\" to \" + str(|type|)", "llvm_inttoptr").generateCode(handle)

#ptr -> int
llvmGenerator("str(|target|) + \" = ptrtoint \" + str(|type|) + \" \" + str(|source|) +\" to \" + str(|type|)", "llvm_ptrtoint").generateCode(handle)

#int -> float
llvmGenerator("str(|target|) + \" = uitofp \" + str(|type|) + \" \" + str(|source|) +\" to \" + str(|type|)", "llvm_uitofp").generateCode(handle)

#trunc
llvmGenerator("str(|target|) + \" = trunc \" + str(|type|) + \" \" + str(|source|) +\" to \" + str(|type|)", "llvm_trunc").generateCode(handle)


#fptrunc
llvmGenerator("str(|target|) + \" = fptrunc \" + str(|type|) + \" \" + str(|source|) +\" to \" + str(|type|)", "llvm_fptrunc").generateCode(handle)


#ret
llvmGenerator("\"ret \" + str(|type|) + \" \" + str(|source|)", "llvm_ret").generateCode(handle)

#call
llvmGenerator("<str(|target|) + \" = \">\"call \" + str(|type|) + \" @\" + str(|name|) + \"(\" <str(|type|) + \" \" + str(|source|)><\", \"+ str(|type|) + \" \" + str(|source|)><\", \"+ str(|type|) + \" \" + str(|source|)><\", \"+ str(|type|) + \" \" + str(|source|)><\", \"+ str(|type|) + \" \" + str(|source|)><\", \"+ str(|type|) + \" \" + str(|source|)><\", \"+ str(|type|) + \" \" + str(|source|)><\", \"+ str(|type|) + \" \" + str(|source|)><\", \"+ str(|type|) + \" \" + str(|source|)>\")\"", "llvm_call").generateCode(handle)

#gep
llvmGenerator("str(|target|) + \" = getelementptr \" + str(|type|) + \" \" + str(|source|) <\", \" + str(|type|) + \" \" + str(|source|)><\", \" + str(|type|) + \" \" + str(|source|)><\", \" + str(|type|) + \" \" + str(|source|)>", "llvm_gep").generateCode(handle)

#phi
llvmGenerator("str(|target|) + \" = phi \" + str(|type|) + \" \" <\" [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \"><\", [ \" + str(|source|) +\", %\" + str(|label|) +\" ] \">", "llvm_phi").generateCode(handle)

